package com.hydra.framework.event.kvo.helper;

import android.util.Log;

import com.hydra.framework.event.kvo.KvoFieldAnnotation;
import com.hydra.framework.event.kvo.KvoMethodAnnotation;
import com.hydra.framework.event.kvo.KvoSource;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import static com.hydra.framework.event.core.helper.EventHelper.getExcludeSystemFields;
import static com.hydra.framework.event.core.helper.EventHelper.getExcludeSystemMethods;
import static com.hydra.framework.event.kvo.Kvo.KVO_LOG_TAG;

/**
 * 做两个事情
 * 1、把Receiver的method信息缓存下来
 * 2、把source的field信息缓存下来
 */
public class KvoHelper {

    public static class KvoMethodNode {
        public Method method;
        public KvoMethodAnnotation methodAnnotation;
    }

    public static class KvoField {
        public Field field;
        public KvoFieldAnnotation fieldAnnotation;
    }

    private static HashMap<Class<?>, HashMap<Class<?>, ArrayList<KvoMethodNode>>> clazzToKvoMethods
            = new HashMap<>();

    private static final Object clazzToKvoMethodsLock = new byte[0];

    //过滤method和field，过滤掉static native 和 private 的
    private static final int NOT_SUPPORT_METHOD_MODIFIERS = Modifier.STATIC | Modifier.NATIVE
            | Modifier.PRIVATE;

    public static ArrayList<KvoMethodNode> getKvoMethods(KvoSource source, Object receiverObj) {
        HashMap<Class<?>, ArrayList<KvoMethodNode>> kvoMethods;

        ArrayList<KvoMethodNode> methodList = null;

        synchronized (clazzToKvoMethodsLock) {
            kvoMethods = clazzToKvoMethods.get(receiverObj.getClass());

            if (kvoMethods != null) {
                methodList = kvoMethods.get(source.getClass());
            } else {
                kvoMethods = new HashMap<>();
                clazzToKvoMethods.put(receiverObj.getClass(), kvoMethods);
            }

            if (methodList != null) {
                return methodList;
            } else {
                methodList = new ArrayList<>();
                kvoMethods.put(source.getClass(), methodList);
            }
        }

        Class<?> receiverClass = receiverObj.getClass();

        List<Method> methods = getExcludeSystemMethods(receiverClass);

        for (Method method : methods) {
            if ((method.getModifiers() & NOT_SUPPORT_METHOD_MODIFIERS) != 0) {
                continue;
            }

            KvoMethodAnnotation annotation = method.getAnnotation(KvoMethodAnnotation.class);

            //如果一个receiver中绑定了多个source的相同key的field并且没有指定sourceClass
            if (annotation != null && (annotation.sourceClass() == KvoSource.class
                    || annotation.sourceClass().isAssignableFrom(source.getClass()))) {

                //这里做了一次校验，即这个field是否存在
                KvoField kvoField = source.declaredKvoField(annotation.name());

                if (kvoField != null) {
                    KvoMethodNode node = new KvoMethodNode();
                    node.method = method;
                    node.methodAnnotation = annotation;

                    methodList.add(node);
                }
            }
        }

        return methodList;
    }

    //每一个KvoSource都是一个Event Dispatcher，每一个字段变更都是在这个dispatcher域中的一个事件
    private static HashMap<Class<? extends KvoSource>, HashMap<String, KvoField>> allKvoSourceFields
            = new HashMap<>();

    private static final Object allKvoSourceFieldsLock = new byte[0];

    public static HashMap<String, KvoField> kvoFieldsContainerFor(Class<? extends KvoSource> clazz) {
        HashMap<String, KvoField> kvoFields;

        synchronized (allKvoSourceFieldsLock) {
            kvoFields = allKvoSourceFields.get(clazz);

            if (kvoFields == null) {
                kvoFields = new HashMap<>();

                List<Field> fields = getExcludeSystemFields(clazz);

                for (Field field : fields) {
                    if ((field.getModifiers() & NOT_SUPPORT_METHOD_MODIFIERS) != 0) {
                        continue;
                    }

                    KvoFieldAnnotation annotation = field.getAnnotation(KvoFieldAnnotation.class);

                    if (annotation != null) {
                        KvoField preField = kvoFields.get(annotation.name());

                        //如果子类和父类中有相同名字的key，使用子类的
                        if (preField == null || preField.field.getDeclaringClass().isAssignableFrom(field.getDeclaringClass())) {
                            KvoField kvoField = new KvoField();
                            kvoField.field = field;
                            kvoField.fieldAnnotation = annotation;

                            kvoFields.put(annotation.name(), kvoField);
                        } else {
                            Log.w(KVO_LOG_TAG, "please don't override the public member fields in subclass, " +
                                    "we will only pick the field in subclass, field name : " + annotation.name() +
                                    "; superclass : " + field.getDeclaringClass() +
                                    "; subclass : " + preField.field.getDeclaringClass());
                        }
                    }
                }

                allKvoSourceFields.put(clazz, kvoFields);
            }
        }

        return kvoFields;
    }
}
